﻿using Prism.Mvvm;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Interop;

namespace VirtualBank.Common.Entity
{
    public enum AccountState
    {
        DEPOSITING,
        DRAWING,
        PROTECTED,
        IDLE,
    }

    public enum BalanceState
    {
        FULL,
        EMPTY,
        NORMAL,
    }

    public class BankAccount 
    {
        private string m_Id;
        public string Id
        {
            get { return m_Id; }
            set 
            { 
                m_Id=value;
                if(InfoTrans!=null)
                {
                    InfoTrans(new AccountInfoMsg(Id, Dollar,balanceState,accountState));
                }                
            }
        }

        private int m_Dollar;
        public int Dollar
        {
            get { return m_Dollar; }
            set 
            { 
                m_Dollar=value;
                UpdateBalanceState();
                if(InfoTrans!=null)
                {
                    InfoTrans(new AccountInfoMsg(Id, Dollar, balanceState, accountState));
                }                          
            }
        }

        private int MaxGetValue = 20;
        private int MaxMoney = 200;
        private int MinMoney = -50;
        private int TotalDrawMoney = 0;
        private AccountState accountState = AccountState.IDLE;
        private BalanceState balanceState;
        private ConcurrentQueue<ServiceMsg> ServiceQueue = new ConcurrentQueue<ServiceMsg>();

        public delegate void InfoTransDelegate(AccountInfoMsg msg);
        public InfoTransDelegate InfoTrans;

        private int DepositMoney(int amount)
        {
            int permitAmount;
            int free = MaxMoney - Dollar;
            permitAmount = Math.Min(amount, free);
            Dollar += permitAmount;
            return permitAmount;
        }

        private int DrawMoney(int amount)
        {
            int permitAmount = amount;
            int free = Dollar - MinMoney;
            permitAmount = Math.Min(permitAmount, MaxGetValue - TotalDrawMoney);
            permitAmount = Math.Min(permitAmount, free);
            TotalDrawMoney += permitAmount;
            Dollar -= permitAmount;
            return permitAmount;
        }

        public void RequireService(ServiceMsg msg)
        {
            //ServiceQueue.Enqueue(msg);
            switch (accountState)
            {
                case AccountState.IDLE:
                    switch (msg.ServiceCode)
                    {
                        case ServiceType.REQ_DEPO:
                            ChangeState(AccountState.DEPOSITING);
                            DepositMoney(msg.Value);
                            break;
                        case ServiceType.REQ_DRAW:
                            if (msg.Value < MaxGetValue - TotalDrawMoney)
                                ChangeState(AccountState.DRAWING);
                            else
                                ChangeState(AccountState.PROTECTED);
                            DrawMoney(msg.Value);
                            break;
                        default:
                            break;
                    }
                    break;
                case AccountState.DEPOSITING:
                    switch (msg.ServiceCode)
                    {
                        case ServiceType.REQ_DEPO:
                            DepositMoney(msg.Value);
                            break;
                        case ServiceType.REQ_QUIT:
                            ChangeState(AccountState.IDLE);
                            break;
                        default:
                            break;
                    }
                    break;
                case AccountState.DRAWING:
                    switch (msg.ServiceCode)
                    {
                        case ServiceType.REQ_DRAW:
                            if (msg.Value < MaxGetValue - TotalDrawMoney)
                                ChangeState(AccountState.DRAWING);
                            else
                                ChangeState(AccountState.PROTECTED);
                            DrawMoney(msg.Value);
                            break;
                        case ServiceType.REQ_QUIT:
                            ChangeState(AccountState.IDLE);
                            TotalDrawMoney = 0;
                            break;
                        default:
                            break;
                    }
                    break;
                case AccountState.PROTECTED:
                    if (msg.ServiceCode == ServiceType.REQ_QUIT)
                    {
                        ChangeState(AccountState.IDLE);
                        TotalDrawMoney = 0;
                    }
                    break;
            }
        }

        private void ServiceQueueHandler()
        {
            ServiceMsg msg;
            while (true)
            {
                if (ServiceQueue.TryDequeue(out msg))
                {
                    switch (accountState)
                    {
                        case AccountState.IDLE:
                            switch (msg.ServiceCode)
                            {
                                case ServiceType.REQ_DEPO:
                                    ChangeState(AccountState.DEPOSITING);
                                    DepositMoney(msg.Value);
                                    break;
                                case ServiceType.REQ_DRAW:
                                    if(msg.Value < MaxGetValue - TotalDrawMoney)
                                        ChangeState(AccountState.DRAWING);
                                    else
                                        ChangeState(AccountState.PROTECTED);
                                    DrawMoney(msg.Value);
                                    break;
                                default:
                                    break;
                            }
                            break;
                        case AccountState.DEPOSITING:
                            switch (msg.ServiceCode)
                            {
                                case ServiceType.REQ_DEPO:
                                    DepositMoney(msg.Value);
                                    break;
                                case ServiceType.REQ_QUIT:
                                    ChangeState(AccountState.IDLE);
                                    break;
                                default:
                                    break;
                            }
                            break;
                        case AccountState.DRAWING:
                            switch (msg.ServiceCode)
                            {
                                case ServiceType.REQ_DRAW:
                                    if (msg.Value < MaxGetValue - TotalDrawMoney)
                                        ChangeState(AccountState.DRAWING);
                                    else
                                        ChangeState(AccountState.PROTECTED);
                                    DrawMoney(msg.Value);
                                    break;
                                case ServiceType.REQ_QUIT:
                                    ChangeState(AccountState.IDLE);
                                    TotalDrawMoney = 0;
                                    break;
                                default:
                                    break;
                            }
                            break;
                        case AccountState.PROTECTED:
                            if (msg.ServiceCode == ServiceType.REQ_QUIT)
                            {
                                ChangeState(AccountState.IDLE);
                                TotalDrawMoney = 0;
                            }
                            break;
                    }
                }
                //Thread.Sleep(10);
            }
        }

        private void ServiceQueueHandler(ServiceMsg msg)
        {
            switch (accountState)
            {
                case AccountState.IDLE:
                    switch (msg.ServiceCode)
                    {
                        case ServiceType.REQ_DEPO:
                            ChangeState(AccountState.DEPOSITING);
                            DepositMoney(msg.Value);
                            break;
                        case ServiceType.REQ_DRAW:
                            if (msg.Value < MaxGetValue - TotalDrawMoney)
                                ChangeState(AccountState.DRAWING);
                            else
                                ChangeState(AccountState.PROTECTED);
                            DrawMoney(msg.Value);
                            break;
                        default:
                            break;
                    }
                    break;
                case AccountState.DEPOSITING:
                    switch (msg.ServiceCode)
                    {
                        case ServiceType.REQ_DEPO:
                            DepositMoney(msg.Value);
                            break;
                        case ServiceType.REQ_QUIT:
                            ChangeState(AccountState.IDLE);
                            break;
                        default:
                            break;
                    }
                    break;
                case AccountState.DRAWING:
                    switch (msg.ServiceCode)
                    {
                        case ServiceType.REQ_DRAW:
                            if (msg.Value < MaxGetValue - TotalDrawMoney)
                                ChangeState(AccountState.DRAWING);
                            else
                                ChangeState(AccountState.PROTECTED);
                            DrawMoney(msg.Value);
                            break;
                        case ServiceType.REQ_QUIT:
                            ChangeState(AccountState.IDLE);
                            TotalDrawMoney = 0;
                            break;
                        default:
                            break;
                    }
                    break;
                case AccountState.PROTECTED:
                    if (msg.ServiceCode == ServiceType.REQ_QUIT)
                    {
                        ChangeState(AccountState.IDLE);
                        TotalDrawMoney = 0;
                    }
                    break;
            }
        }

        private void ChangeState(AccountState accountState)
        {
            this.accountState = accountState;
            //InfoTrans(new AccountInfoMsg(Id, Dollar, balanceState, accountState));
        }

        private void UpdateBalanceState()
        {
            if (Dollar <= MinMoney)
            {
                balanceState=BalanceState.EMPTY;
            }
            else if (Dollar >= MaxMoney)
            {
                balanceState = BalanceState.FULL;
            }
            else
            {
                balanceState = BalanceState.NORMAL;
            }
            if(InfoTrans!=null)
            {
                InfoTrans(new AccountInfoMsg(Id, Dollar, balanceState, accountState));
            }            
        }

        public AccountInfoMsg CheckAccountState()
        {
            AccountInfoMsg msg = new AccountInfoMsg(Id, Dollar, balanceState, accountState);
            return msg;
        }

        public BankAccount()
        {
            m_Dollar = 0;
            //Task QueueHandlerTask = new Task(ServiceQueueHandler);
            //QueueHandlerTask.Start();
        }

        public BankAccount(string id)
        {
            this.Id = id;
            this.Dollar = 0;
            m_Dollar = 0;
            //Task QueueHandlerTask = new Task(ServiceQueueHandler);
            //QueueHandlerTask.Start();
        }
    }
}
